﻿/*  CLR版本: 4.0.30319.18063
 * 系统时间: 2014/10/31 14:20:43
 * 创建年份: 2014
 *     作者: 程炜.Snail
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Quick.Communicator
{
    public class TCPClient
    {
        #region 字段
        /// <summary>
        /// 客户端与服务器之间的会话类
        /// </summary>
        private Session m_Session;
        /// <summary>
        /// 客户端是否已经连接服务器
        /// </summary>
        private bool m_IsConnected = false;
        /// <summary>
        /// 连接的服务器的端口
        /// </summary>
        private IPEndPoint m_ServerIPEndPoint;
        /// <summary>
        /// 接收数据缓冲区大小10K
        /// </summary>
        public const int DefaultBufferSize = 10 * 1024;
        /// <summary>
        /// 报文解析器
        /// </summary>
        private IResolver m_Resolver;

        /// <summary>
        /// TCPClient使用的Socket
        /// </summary>
        private Socket m_Socket;
        /// <summary>
        /// 本地Socket使用的IP地址,端口
        /// </summary>
        private IPEndPoint m_LocalIpEndPoint;
        /// <summary>
        /// 发送数据的 SocketAsyncEventArgs
        /// </summary>
        private SocketAsyncEventArgs m_SendSocketAsyncEventArgs;
        /// <summary>
        /// 接收数据的 SocketAsyncEventArgs
        /// </summary>
        private SocketAsyncEventArgs m_ReceiveSocketAsyncEventArgs;
        /// <summary>
        /// 客户端接收到的服务器总字节数
        /// </summary>
        private Int64 m_TotalBytesRead;
        /// <summary>
        /// 线程锁
        /// </summary>
        ManualResetEvent m_ManualResetEvent = new ManualResetEvent(false);
        #endregion

        #region 事件
        /// <summary>
        /// 信号接收完成 事件
        /// </summary>
        public event ReceiveDataEndEventHandler ReceiveDataEnd;
        /// <summary>
        /// 连接断开事件
        /// </summary>
        public event NetEventHandler DisConnectedServer;
        /// <summary>
        /// 已经连接服务器事件
        /// </summary>
        public event NetEventHandler ConnectedServer;
        /// <summary>
        /// 数据报文发送完成 事件
        /// </summary>
        public event SendDataEndEventHandler SendDataEnd;
        /// <summary>
        /// 服务器接收的客户端字节总数
        /// </summary>
        public event ReceiveTotalBytesEventHandler ReceiveTotalBytes;

        #endregion

        #region 属性
        /// <summary>
        /// 客户端是否与服务器连接上
        /// </summary>
        public bool IsConnected
        {
            get { return m_IsConnected; }
        }
        /// <summary>
        /// 报文解析器
        /// </summary>
        public IResolver Resolver
        {
            get { return m_Resolver; }
            set { m_Resolver = value; }
        }
        #endregion

        /// <summary>
        /// 构造函数,使用一个特定的编码器来初始化
        /// </summary>
        /// <param name="coder">报文编码器</param>
        /// <param name="resolver">报文解析器</param>
        public TCPClient(IResolver resolver)
            : this(null, resolver)
        { }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ipept">指定本地端口</param>
        /// <param name="resolver">报文解析器</param>
        public TCPClient(IPEndPoint ipept, IResolver resolver)
        {
            if (null != ipept)
            {
                this.m_LocalIpEndPoint = ipept;
            }            
            m_Resolver = resolver;
            m_SendSocketAsyncEventArgs = new SocketAsyncEventArgs();
            m_SendSocketAsyncEventArgs.DisconnectReuseSocket = true;
            m_ReceiveSocketAsyncEventArgs = new SocketAsyncEventArgs();
            m_ReceiveSocketAsyncEventArgs.DisconnectReuseSocket = true;

            m_SendSocketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArg_Completed);
            m_ReceiveSocketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArg_Completed);
            m_ReceiveSocketAsyncEventArgs.SetBuffer(new byte[DefaultBufferSize], 0, DefaultBufferSize);
        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="ip">服务器IP地址</param>
        /// <param name="port">服务器端口</param>
        public virtual bool Connect(string ip, int port)
        {
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
            return Connect(iep);
        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        public virtual bool Connect(IPEndPoint iep)
        {
            if (m_IsConnected)
            {
                return m_IsConnected;
            }
            m_ManualResetEvent.Reset();
            m_TotalBytesRead = 0L;
            m_ServerIPEndPoint = iep;
            m_Socket = new Socket(iep.AddressFamily , SocketType.Stream, ProtocolType.Tcp);
            m_ServerIPEndPoint = iep;

            if (null != m_LocalIpEndPoint)
            {
                m_Socket.Bind(m_LocalIpEndPoint);
            }
            if (m_SendSocketAsyncEventArgs.DisconnectReuseSocket)
            {
                m_SendSocketAsyncEventArgs.RemoteEndPoint = iep;
                m_SendSocketAsyncEventArgs.UserToken = m_Socket;
                m_SendSocketAsyncEventArgs.SetBuffer(null, 0, 0);
            }
            if (m_ReceiveSocketAsyncEventArgs.DisconnectReuseSocket)
            {
                m_ReceiveSocketAsyncEventArgs.RemoteEndPoint = iep;
                m_ReceiveSocketAsyncEventArgs.UserToken = m_Socket;
            }
            if (!m_Socket.ConnectAsync(m_SendSocketAsyncEventArgs))
            {
                if (m_SendSocketAsyncEventArgs.SocketError == SocketError.Success)
                {
                    ProcessConnect(m_SendSocketAsyncEventArgs);
                }
                else
                {
                    ProcessConnect(m_ReceiveSocketAsyncEventArgs);
                }
            }
            m_ManualResetEvent.WaitOne(5000);
            return m_IsConnected;
        }
        /// <summary>
        /// 异步完成操作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SocketEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    ProcessConnect(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Disconnect:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                //default:
                //    throw new Exception("Invalid operation completed");
            }
        }
        /// <summary>
        /// SocketEventArg Completed 事件  SocketAsyncOperation.Connect
        /// </summary>
        private void ProcessConnect(SocketAsyncEventArgs e)
        {
            if (null != e && e.SocketError == SocketError.Success)
            {
                m_IsConnected = true;
                //成功链接到服务器,创建Session
                m_Session = new Session(m_ReceiveSocketAsyncEventArgs);
                OnConnectedServer(this, new NetEventArgs(m_Session));
                //开始接收数据
                if (!this.m_Socket.ReceiveAsync(m_ReceiveSocketAsyncEventArgs))
                {
                    ProcessReceive(m_ReceiveSocketAsyncEventArgs);
                }
                //链接服务器事件通知
            }
            else
            {
                Close();
            }
            m_ManualResetEvent?.Set();
        }
        /// <summary>
        /// SocketEventArg Completed 事件  SocketAsyncOperation.Receive
        /// </summary>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            // 检查远程主机是否关闭了连接。
            Socket userTokenSocket = e.UserToken as Socket;
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //增加服务器接收的总字节数
                Interlocked.Add(ref m_TotalBytesRead, e.BytesTransferred);
                OnReceiveTotalBytes(this, new ReceiveTotalBytesEventArgs(m_TotalBytesRead));
                //将接收到的数据暂存到Session中, 如果有相关数据,则进行拼接
                if (null != ReceiveDataEnd)
                {
                    if (null == m_Session)
                    {
                        return;
                    }
                    m_Session.LastConnectionTime = DateTime.Now;

                    byte[] receivedByte = new byte[e.BytesTransferred];
                    Array.Copy(e.Buffer, e.Offset, receivedByte, 0, e.BytesTransferred);

                    // 如果没有报文结束符标记,则直接进行数据转发
                    if (string.IsNullOrEmpty(m_Resolver.EndTag) && string.IsNullOrEmpty(m_Resolver.HexStringEndTag))
                    {
                        //深拷贝,为了保持Datagram的对立性
                        ICloneable copySession = (ICloneable)m_Session;
                        Session clientSession = (Session)copySession.Clone();
                        clientSession.DatagramByte = receivedByte;
                        clientSession.Datagram = this.m_Resolver.DatagramDesEncoding(receivedByte);
                        //发布一个报文消息
                        OnReceiveSignalEnd(this, new NetEventArgs(clientSession));
                    }
                    else //存在报文结束符,则需要进行分割
                    {
                        string receivedData = this.m_Resolver.ByteToHexString(receivedByte);

                        if (!string.IsNullOrEmpty(m_Session.SpareDatagram) && m_Session.SpareDatagram.Length != 0)
                        {
                            //加上最后一次通讯剩余的报文片断
                            receivedData = string.Concat(m_Session.SpareDatagram, ",", receivedData);
                        }
                        string[] recvDatagrams = m_Resolver.Resolve(ref receivedData);
                        //剩余的代码片断,下次接收的时候使用
                        m_Session.SpareDatagram = receivedData;
                        receivedData = null;
                        foreach (string newDatagram in recvDatagrams)
                        {
                            //深拷贝,为了保持Datagram的对立性
                            ICloneable copySession = (ICloneable)m_Session;
                            Session clientSession = (Session)copySession.Clone();
                            clientSession.DatagramByte = this.m_Resolver.HexStringToByte(newDatagram);
                            clientSession.Datagram = this.m_Resolver.DatagramDesEncoding(clientSession.DatagramByte);
                            //发布一个报文消息
                            OnReceiveSignalEnd(this, new NetEventArgs(clientSession));
                        }
                    } //end of if(ReceiveSignalEnd != null)
                }
                //收完数据后,进行下一次接收
                if (!userTokenSocket.ReceiveAsync(e))
                {
                    ProcessReceive(e);
                }
            }
            else
            {
                Close();
            }
        }
        /// <summary>
        /// SocketEventArg Completed 事件 SocketAsyncOperation.Send
        /// </summary>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            SendStateEventArgs args = null;
            if (e.SocketError == SocketError.Success)
            {   //消息发送成功
                m_SendSocketAsyncEventArgs.SetBuffer(null, 0, 0);
                args = new SendStateEventArgs(SendState.Success);
                if (null == m_Session)
                {
                    m_Session = new Session(m_ReceiveSocketAsyncEventArgs);
                }
                m_Session.LastConnectionTime = DateTime.Now;
            }
            else
            {
                args = new SendStateEventArgs(SendState.Failure);
            }
            OnSendDataEnd(this, args);
        }

        /// <summary>
        /// 信号接收完成 事件,通知相关订阅者
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnReceiveSignalEnd(object sender, NetEventArgs e)
        {
            if (null != ReceiveDataEnd)
            {
                Delegate[] delegateList = ReceiveDataEnd.GetInvocationList();
                foreach (ReceiveDataEndEventHandler handler in delegateList)
                {
                    handler.BeginInvoke(sender, e, null, null);
                }
            }
        }
        /// <summary>
        /// 触发关闭Socket
        /// </summary>
        private void OnDisConnectedServer()
        {
            //服务器无对应程式接收数据
            NetEventArgs nea = null;
            m_IsConnected = false;
            try
            {
                if (null == m_Session)
                {
                    nea = new NetEventArgs(new Session(m_ServerIPEndPoint, 1));
                }
                else
                {
                    nea = new NetEventArgs(m_Session);
                }
                if (DisConnectedServer != null)
                {
                    DisConnectedServer(this, nea);
                }
            }
            catch
            { }
        }
        /// <summary>
        /// 数据报文发送完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSendDataEnd(object sender, SendStateEventArgs e)
        {
            if (SendDataEnd != null)
            {
                Delegate[] delegateList = SendDataEnd.GetInvocationList();
                foreach (SendDataEndEventHandler handler in delegateList)
                {
                    handler.BeginInvoke(sender, e, null, null);
                }
            }
        }
        /// <summary>
        /// 服务器接收字节总数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnReceiveTotalBytes(object sender, ReceiveTotalBytesEventArgs e)
        {
            if (null != ReceiveTotalBytes)
            {
                Delegate[] delegateList = ReceiveTotalBytes.GetInvocationList();
                foreach (ReceiveTotalBytesEventHandler handler in delegateList)
                {
                    handler.BeginInvoke(sender, e, null, null);
                }
            }
        }
        /// <summary>
        /// 连接上服务器事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConnectedServer(object sender,NetEventArgs e)
        {
            if (null != ConnectedServer)
            {
                Delegate[] delegateList = ConnectedServer.GetInvocationList();
                foreach (NetEventHandler handler in delegateList)
                {
                    handler.BeginInvoke(sender, e, null, null);
                }
            }
        }
        public void Close()
        {
            if (m_IsConnected)
            {
                try
                {
                    m_Socket.Shutdown(SocketShutdown.Both);
                }
                catch (Exception)
                { }
            }
            m_Socket.Close();
            OnDisConnectedServer();
        }
        /// <summary>
        /// 发送数据报文
        /// </summary>
        /// <param name="data"></param>
        public virtual void AsyncSend(string datagram, bool isAddEndTag = true)
        {
            //获得报文的编码字节
            byte[] bt = this.m_Resolver.DatagramEncodingBarringEngTag(datagram);
            AsyncSend(bt, isAddEndTag);
        }
        /// <summary>
        /// 异步发送数据报文
        /// </summary>
        /// <param name="data"></param>
        public virtual void AsyncSend(byte[] data, bool isAddEndTag = true)
        {
            if (null == data || data.Length == 0)
            {
                return;
            }
            if (!m_IsConnected)
            {
                throw (new Exception("没有连接服务器，不能发送数据"));
            }
            try
            {
                if (isAddEndTag)
                {
                    data = this.m_Resolver.DatagramAddEndTag(data);
                }
                //m_SendSocketAsyncEventArgs.SetBuffer(data, 0, data.Length);
                //if (!m_Socket.SendAsync(m_SendSocketAsyncEventArgs))
                //{
                //    m_SendSocketAsyncEventArgs.SetBuffer(null, 0, 0);
                //    OnSendDataEnd(this, new SendStateEventArgs(SendState.Success));
                //}
                //Socket socket = m_ReceiveSocketAsyncEventArgs.UserToken as Socket;
                m_Socket.Send(data);
                //SendDataToServerEnd();
                //socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendDataToServerEnd), socket);
            }
            catch (SocketException se)
            {   
                if (se.ErrorCode == 10054)
                {
                    OnDisConnectedServer();
                }
                OnSendDataEnd(this, new SendStateEventArgs(SendState.Failure));
            }
            catch (Exception ex)
            {
                OnSendDataEnd(this, new SendStateEventArgs(SendState.Failure));
                //_IsConnected = false;
                //Send(data);
            }
        }
        ///// <summary>
        ///// 数据发送完成处理函数
        ///// </summary>
        ///// <param name="iar"></param>
        //protected virtual void SendDataToServerEnd(IAsyncResult iar)
        //{
        //    try
        //    {
        //        Socket remote = (Socket)iar.AsyncState;
        //        int sent = remote.EndSend(iar);
        //        OnSendDataEnd(this, new SendStateEventArgs(SendState.Success));
        //    }
        //    catch (ObjectDisposedException odex)
        //    {
        //        OnSendDataEnd(this, new SendStateEventArgs(SendState.Failure));
        //    }
        //    catch (Exception ex)
        //    {
        //        OnSendDataEnd(this, new SendStateEventArgs(SendState.Failure));
        //    }
        //}
    }
}
